# -*- coding:utf-8 -*-

import json
from fastapi import Depends, HTTPException, status, Request
from fastapi.security import SecurityScopes

from app.models.request.user import User
from app.models.request.role import Role, Permision, RolePersision
from app.repository.user import UserRepository
from app.repository.role import RoleRepository
from app.service.auth import get_token_data

from datetime import datetime
# 创建自定义编码器
class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()  # 将 datetime 转换为字符串
        return super().default(obj)


class UserService:
    def __init__(self, repo: UserRepository=Depends(UserRepository), role_repo: RoleRepository = Depends(RoleRepository)):
        self.repo = repo
        self.role_repo = role_repo

    async def get_user_by_id(self, id: int, redis):
        redis_user = await redis.get(f"user_id:{id}")
        if redis_user is not None:
            print("从redis获取用户信息")
            return json.loads(redis_user)
        db_user = await self.repo.get_by_id(id)
        if db_user is None:
            raise HTTPException(status_code=404, detail="User not found")
        print("从数据库获取用户信息")
        await redis.set(f"user_id:{id}", json.dumps(db_user.single_to_dict(), ensure_ascii=False), ex=60)
        return db_user

    async def get_role_by_user_id(self, user_id: int):
        return (await self.repo.get_ref_by_ids([user_id], "tb_role", "user_role", Role))[user_id]

    async def get_persision_by_user_id(self, user_id: int):
        roles = (await self.repo.get_ref_by_ids([user_id], "tb_role", "user_role", Role))[user_id]
        if roles:
            role_ids = [role.id for role in roles]
            permisions = await self.role_repo.get_ref_by_ids(role_ids, "tb_permision", "role_permision", Permision)
            return [item for sub in permisions.values() for item in sub] # [permisions[role_id] for role_id in role_ids if permisions[role_id]]
        return []

    async def get_role_persision_by_user_id(self, user_id: int):
        roles = (await self.repo.get_ref_by_ids([user_id], "tb_role", "user_role", Role))[user_id]
        if roles:
            role_ids = [role.id for role in roles]
            permisions = await self.role_repo.get_ref_by_ids(role_ids, "tb_permision", "role_permision", Permision)
            roles = [RolePersision.from_orm(role) for role in roles]
            for role in roles:
                role.permisions = permisions[role.id]
            return roles
        return []


async def check_login_user(request: Request, security_scopes: SecurityScopes, token_data: dict=Depends(get_token_data), 
    repo: UserRepository = Depends(UserRepository), user_service: UserService = Depends(UserService)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效凭证",
    )
    if token_data is None:
        raise credentials_exception
    db_user = await repo.get_user_by_username(token_data["sub"])
    if db_user is None:
        raise credentials_exception
    if db_user.status == 0:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="用户未激活",
        )
    if db_user.status == 5:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="用户已被禁用",
        )
    # 判断是否设置了权限域
    if security_scopes.scopes:
        # 未查询用户是否有对应权限
        roles = await user_service.get_role_persision_by_user_id(db_user.id)
        is_pass = 'admin' in [role.name for role in roles]
        if not is_pass:
            for scope in security_scopes.scopes:
                if scope in [persision.name for role in roles for persision in role.permisions]:
                    is_pass = True
                    break
        if not is_pass:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not permissions",
                headers={"scopes": security_scopes.scope_str},
            )
    # 缓存用户ID
    request.state.user_id = db_user.id

